#include "DHVideo.h"

extern "C"{
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavdevice/avdevice.h>
    #include <libavfilter/avfilter.h>
    #include <libavfilter/buffersrc.h>
    #include <libavfilter/buffersink.h>
    #include <libavutil/avutil.h>
    #include <libavutil/pixfmt.h>
    #include <libswresample/swresample.h>
    #include <libswscale/swscale.h>
    #include <libavutil/imgutils.h>
    #include <libavutil/hwcontext.h>
}

#include <thread>
#include <mutex>
#include <list>
#include <iostream>
#include <libyuv.h>

extern void test_start();
extern double test_check();

typedef struct tagStEncode
{
    int frame_encode_index = 0;
    SwsContext* sws = nullptr;
    AVPacket *pkt = nullptr;
    AVCodecContext* ce=nullptr;
    AVCodec *codecEncode=nullptr;
    AVFrame *frame = nullptr, *swframe = nullptr;
    void (*recv)(void* obj, unsigned char* data, int& len);

    //硬件加速
    AVFrame *hwframe = nullptr;
    AVBufferRef *hw_device_ctx = nullptr; 

    void* user_obj =nullptr;
}_StEncode;
extern AVPixelFormat g_format;
extern int g_align,g_gop;

#pragma region CPU编码
void Encode(void** handle, AVFrame *frame)
{
    _StEncode* StEncode = (_StEncode*)*handle;
    if(!StEncode || !StEncode->ce) return;
    int ret = avcodec_send_frame(StEncode->ce, frame);
    if  (ret<0)  return;
    while (ret >= 0)
    {
        if (!StEncode->pkt) return;
        ret = avcodec_receive_packet(StEncode->ce, StEncode->pkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            break;
        else if (ret < 0)
            return;

        if (StEncode->pkt!=nullptr && StEncode->pkt->size>0) {
            StEncode->recv(StEncode->user_obj, StEncode->pkt->data, StEncode->pkt->size);
        }

        if(StEncode->pkt) 
            av_packet_unref(StEncode->pkt);
    }
}
void SWBeginEncode(void** handle, void* obj, int width, int height, int framerate, __int64 bit_rate,int code, void(*encode)(void* obj, unsigned char* data, int& len))
{
    _StEncode* StEncode = (_StEncode*)*handle;
    StEncode->frame_encode_index = 0;
	StEncode->codecEncode = avcodec_find_encoder((AVCodecID)code);
    if(!StEncode->codecEncode)  {
        av_log(nullptr, AV_LOG_ERROR, "failed to find encoder\n");
        return;
    }
    StEncode->ce = avcodec_alloc_context3(StEncode->codecEncode);
    if (!StEncode->ce)  {
        av_log(nullptr, AV_LOG_ERROR, "failed to allocate the encoder context\n");
        return;
    }

    StEncode->ce->bit_rate = bit_rate;
    /* resolution must be a multiple of two */
    StEncode->ce->width = width;
    StEncode->ce->height = height;
    /* frames per second */
    StEncode->ce->time_base = av_make_q(1, framerate);
    StEncode->ce->framerate = av_make_q(framerate, 1);
    StEncode->ce->pix_fmt = AV_PIX_FMT_YUV420P;

    StEncode->ce->gop_size = g_gop;
    StEncode->ce->max_b_frames = 0;

	// if (x264){
	// 	av_opt_set(StEncode->ce->priv_data, "preset", "veryfast", 0);
	// 	av_opt_set(StEncode->ce->priv_data, "tune", "zerolatency", 0);
	// 	av_opt_set(StEncode->ce->priv_data, "profile", "main", 0);
	// }  
    int ret = 0;
    if((ret = avcodec_open2(StEncode->ce, StEncode->codecEncode, NULL))<0){
        av_log(nullptr, AV_LOG_ERROR, "Failed to open encoder\r\n");
        return;
    }

    StEncode->frame = av_frame_alloc(); 
    if(StEncode->frame==nullptr){
        av_log(nullptr, AV_LOG_ERROR, "failed to allocate the encoder frame\n");
    }
    StEncode->swframe = av_frame_alloc();
    if(StEncode->swframe==nullptr){
        av_log(nullptr, AV_LOG_ERROR, "failed to allocate the encoder frame\n");
    }
    StEncode->pkt = av_packet_alloc();
    if(StEncode->pkt==nullptr){
        av_log(nullptr, AV_LOG_ERROR, "failed to allocate the encoder packet\n");
    }
    StEncode->frame->format = StEncode->ce->pix_fmt;
    StEncode->swframe->format = AV_PIX_FMT_YUV420P;
    StEncode->frame->width  = StEncode->swframe->width = StEncode->ce->width;
    StEncode->frame->height = StEncode->swframe->height = StEncode->ce->height; 
    if(av_frame_get_buffer(StEncode->frame, g_align)<0)
        av_frame_get_buffer(StEncode->frame, 1);
    if(av_frame_get_buffer(StEncode->swframe, g_align)<0)
        av_frame_get_buffer(StEncode->swframe, 1);

    StEncode->sws = sws_getContext(StEncode->ce->width, StEncode->ce->height, g_format,StEncode->ce->width, 
        StEncode->ce->height, StEncode->ce->pix_fmt,SWS_FAST_BILINEAR, NULL, NULL, NULL);
}
void SWImageEncode(void** handle, unsigned char* image_data, int image_len)
{
#ifdef TEST
    test_start();
#endif
    _StEncode* StEncode = (_StEncode*)*handle;
    if (!StEncode->frame || !StEncode->swframe)  return;
    StEncode->frame->pts = StEncode->swframe->pts = StEncode->frame_encode_index;
    StEncode->frame_encode_index++;

    if(AV_PIX_FMT_GRAY8==g_format){
        memcpy(StEncode->swframe->data[0],image_data,image_len);
        memset(StEncode->swframe->data[1],128,image_len/2);
    }
    else{
        if(av_image_fill_arrays(StEncode->frame->data,StEncode->frame->linesize, image_data, g_format, 
            StEncode->ce->width, StEncode->ce->height,g_align)<0)
            av_image_fill_arrays(StEncode->frame->data,StEncode->frame->linesize, image_data, g_format, 
                StEncode->ce->width, StEncode->ce->height,1);
        switch(g_format){
            case AV_PIX_FMT_ARGB:
                libyuv::ARGBToI420(StEncode->frame->data[0],StEncode->frame->linesize[0],
                    StEncode->swframe->data[0],StEncode->swframe->linesize[0],
                    StEncode->swframe->data[2],StEncode->swframe->linesize[2],
                    StEncode->swframe->data[1],StEncode->swframe->linesize[1],
                    StEncode->frame->width,StEncode->frame->height);
                break;
            default:
                sws_scale(StEncode->sws,(const uint8_t **)StEncode->frame->data,StEncode->frame->linesize,
                    0,StEncode->ce->height, StEncode->swframe->data,StEncode->swframe->linesize);
                break;
        }
    }
#ifdef TEST
    double fft = test_check();
#endif
    Encode(handle, StEncode->swframe);
#ifdef TEST
    printf("%f-%f ms\r\n",fft,test_check());
#endif
}
void SWEndEncode(void** handle,bool bDelHandle = true)
{
    _StEncode* StEncode = (_StEncode*)*handle;
    Encode(handle, nullptr);
    if(StEncode->ce != nullptr) 
        avcodec_free_context(&StEncode->ce);
    if(StEncode->frame != nullptr){
        av_frame_unref(StEncode->frame);
        av_frame_free(&StEncode->frame);
    }
    if(StEncode->swframe != nullptr){
        av_frame_unref(StEncode->swframe);
        av_frame_free(&StEncode->swframe);
    }
    if(StEncode->pkt != nullptr)
        av_free_packet(StEncode->pkt);
    if(StEncode->sws != nullptr)
        sws_freeContext(StEncode->sws);
    if(StEncode && bDelHandle) {
        delete StEncode;
        *handle =  nullptr;
    }
}
#pragma endregion

#pragma region QSV编码
void QSVBeginEncode(void** handle, void* obj, int width, int height, int framerate, __int64 bit_rate,int code, void(*encode)(void* obj, unsigned char* data, int& len))
{
    _StEncode* StEncode = (_StEncode*)*handle;
    StEncode->frame_encode_index = 0;
	StEncode->codecEncode = (code==AV_CODEC_ID_H264?avcodec_find_encoder_by_name("h264_qsv"):
		(code==AV_CODEC_ID_HEVC?avcodec_find_encoder_by_name("hevc_qsv"):
        avcodec_find_encoder((AVCodecID)code)));
    if(!StEncode->codecEncode)  {
        av_log(nullptr, AV_LOG_ERROR, "failed to find encoder\n");
        return;
    }
    StEncode->ce = avcodec_alloc_context3(StEncode->codecEncode);
    if (!StEncode->ce)  {
        av_log(nullptr, AV_LOG_ERROR, "failed to allocate the encoder context\n");
        return;
    }

    StEncode->ce->bit_rate = bit_rate;
    /* resolution must be a multiple of two */
    StEncode->ce->width = width;
    StEncode->ce->height = height;
    /* frames per second */
    StEncode->ce->time_base = av_make_q(1, framerate);
    StEncode->ce->framerate = av_make_q(framerate, 1);
    StEncode->ce->pix_fmt = AV_PIX_FMT_QSV;

    StEncode->ce->gop_size = g_gop;
    StEncode->ce->max_b_frames = 0;

    // if(x264){
    //     av_opt_set(StEncode->ce->priv_data, "preset", "veryfast", 0);
    //     av_opt_set(StEncode->ce->priv_data, "tune", "zerolatency", 0);
    //     av_opt_set(StEncode->ce->priv_data, "profile", "main", 0);
    // }
    if(StEncode->hw_device_ctx){
        AVBufferRef *hw_frames_ref;
        AVHWFramesContext *frames_ctx = NULL;
        if (!(hw_frames_ref = av_hwframe_ctx_alloc(StEncode->hw_device_ctx))){
            av_log(nullptr, AV_LOG_ERROR, "failed to allocate the encoder hardware frame\n");
            return;
        }
        
        frames_ctx = (AVHWFramesContext *)(hw_frames_ref->data);
        frames_ctx->format    = AV_PIX_FMT_QSV;
        frames_ctx->sw_format = AV_PIX_FMT_NV12;
        frames_ctx->width     = width;
        frames_ctx->height    = height;
        frames_ctx->initial_pool_size = 20;

        int err = 0;
        if ((err = av_hwframe_ctx_init(hw_frames_ref)) < 0) {
            av_log(nullptr, AV_LOG_ERROR, "failed to initial the encoder hardware context\n");
            av_buffer_unref(&hw_frames_ref);
            return;
        }
        StEncode->ce->hw_frames_ctx = av_buffer_ref(hw_frames_ref);
        if(StEncode->ce->hw_frames_ctx==nullptr){
            av_log(nullptr, AV_LOG_ERROR, "failed to allocate the encoder hardware buffer\n");
        }
        av_buffer_unref(&hw_frames_ref);
    }

    int ret = 0;
    if((ret = avcodec_open2(StEncode->ce, StEncode->codecEncode, NULL))<0){
        av_log(nullptr, AV_LOG_ERROR, "Failed to open encoder\r\n");
        return;
    }

    StEncode->hwframe = av_frame_alloc();
    if(StEncode->hwframe==nullptr){
        av_log(nullptr, AV_LOG_ERROR, "failed to allocate the encoder hardware frame\n");
    }
    StEncode->frame = av_frame_alloc();
    if(StEncode->frame==nullptr){
        av_log(nullptr, AV_LOG_ERROR, "failed to allocate the encoder frame\n");
    }
    StEncode->swframe = av_frame_alloc();
    if(StEncode->swframe==nullptr){
        av_log(nullptr, AV_LOG_ERROR, "failed to allocate the encoder frame\n");
    }
    StEncode->pkt = av_packet_alloc();
    if(StEncode->pkt==nullptr){
        av_log(nullptr, AV_LOG_ERROR, "failed to allocate the encoder packet\n");
    }
    StEncode->frame->format = g_format;
    StEncode->swframe->format = AV_PIX_FMT_NV12;
    StEncode->frame->width  = StEncode->swframe->width = StEncode->ce->width;
    StEncode->frame->height = StEncode->swframe->height = StEncode->ce->height; 

    if(av_frame_get_buffer(StEncode->frame, g_align)<0)
        av_frame_get_buffer(StEncode->frame, 1);
    if(av_frame_get_buffer(StEncode->swframe, g_align)<0)
        av_frame_get_buffer(StEncode->swframe, 1);
    av_hwframe_get_buffer(StEncode->ce->hw_frames_ctx, StEncode->hwframe, 0);

    StEncode->sws = sws_getContext(StEncode->ce->width, StEncode->ce->height, g_format,StEncode->ce->width, 
        StEncode->ce->height, AV_PIX_FMT_NV12,SWS_FAST_BILINEAR, NULL, NULL, NULL);
}

void QSVImageEncode(void** handle, unsigned char* image_data, int image_len)
{
#ifdef TEST
    test_start();
#endif
    _StEncode* StEncode = (_StEncode*)*handle;
    if (!StEncode->frame || !StEncode->swframe)  return;
    StEncode->frame->pts = StEncode->swframe->pts = StEncode->frame_encode_index;
    StEncode->frame_encode_index++;

    if(g_format==AV_PIX_FMT_GRAY8){
        memcpy(StEncode->swframe->data[0],image_data,image_len);
        memset(StEncode->swframe->data[1],128,image_len/2);
    }
    else{
        if(av_image_fill_arrays(StEncode->frame->data,StEncode->frame->linesize, image_data, g_format, 
            StEncode->ce->width, StEncode->ce->height,g_align)<0)
            av_image_fill_arrays(StEncode->frame->data,StEncode->frame->linesize, image_data, g_format, 
                StEncode->ce->width, StEncode->ce->height,1);
        switch(g_format){
            case AV_PIX_FMT_ARGB:
                libyuv::ARGBToNV12(StEncode->frame->data[0],StEncode->frame->linesize[0],
                    StEncode->swframe->data[0],StEncode->swframe->linesize[0],
                    StEncode->swframe->data[1],StEncode->swframe->linesize[1],
                    StEncode->frame->width,StEncode->frame->height);
                break;
            default:
                sws_scale(StEncode->sws,(const uint8_t **)StEncode->frame->data,StEncode->frame->linesize,
                    0,StEncode->ce->height, StEncode->swframe->data,StEncode->swframe->linesize);
                break;
        }
    }
#ifdef TEST
    double fft =  test_check();
#endif
    if(av_hwframe_transfer_data(StEncode->hwframe, StEncode->swframe, 0)>=0)
        Encode(handle,StEncode->hwframe);
#ifdef TEST
    printf("%f-%f ms\r\n",fft,test_check());
#endif
}
void QSVEndEncode(void** handle,bool bDelHandle = true)
{
    _StEncode* StEncode = (_StEncode*)*handle;
    Encode(handle, nullptr);
    if(StEncode->ce != nullptr) 
        avcodec_free_context(&StEncode->ce);
    if(StEncode->pkt != nullptr)
        av_free_packet(StEncode->pkt);
    if(StEncode->frame != nullptr){
        av_frame_unref(StEncode->frame);
        av_frame_free(&StEncode->frame);
    }
    if(StEncode->swframe != nullptr){
        av_frame_unref(StEncode->swframe);
        av_frame_free(&StEncode->swframe);
    }
    if(StEncode->hwframe != nullptr){
        av_frame_unref(StEncode->hwframe);
        av_frame_free(&StEncode->hwframe);
    }
    if(StEncode->sws != nullptr)
        sws_freeContext(StEncode->sws);
    if(StEncode->hw_device_ctx!=nullptr)
        av_buffer_unref(&StEncode->hw_device_ctx);
    if(StEncode && bDelHandle) {
        delete StEncode;
        *handle =  nullptr;
    }
}
#pragma endregion

#pragma region CUDA编码
void CUDABeginEncode(void** handle, void* obj, int width, int height, int framerate, __int64 bit_rate,int code, void(*encode)(void* obj, unsigned char* data, int& len))
{
    _StEncode* StEncode = (_StEncode*)*handle;
    StEncode->frame_encode_index = 0;
	StEncode->codecEncode = (code==AV_CODEC_ID_H264?avcodec_find_encoder_by_name("nvenc_h264"):
		(code==AV_CODEC_ID_H265?avcodec_find_encoder_by_name("nvenc_hevc"):
        avcodec_find_encoder((AVCodecID)code)));
    if(!StEncode->codecEncode){
        av_log(nullptr, AV_LOG_ERROR, "failed to find encoder\n");    
        return;
    }
    StEncode->ce = avcodec_alloc_context3(StEncode->codecEncode);
    if (!StEncode->ce){
        av_log(nullptr, AV_LOG_ERROR, "failed to allocate the encoder context\n");
        return;
    }

    StEncode->ce->bit_rate = bit_rate;
    /* resolution must be a multiple of two */
    StEncode->ce->width = width;
    StEncode->ce->height = height;
    /* frames per second */
    StEncode->ce->time_base = av_make_q(1, framerate);
    StEncode->ce->framerate = av_make_q(framerate, 1);
    StEncode->ce->pix_fmt = AV_PIX_FMT_CUDA;

    StEncode->ce->gop_size = g_gop;
    StEncode->ce->max_b_frames = 0;
    
    // if(x264){
    //     av_opt_set(StEncode->ce->priv_data, "preset", "veryfast", 0);
    //     av_opt_set(StEncode->ce->priv_data, "tune", "zerolatency", 0);
    //     av_opt_set(StEncode->ce->priv_data, "profile", "main", 0);
    // }
    if(StEncode->hw_device_ctx){
        AVBufferRef *hw_frames_ref;
        AVHWFramesContext *frames_ctx = NULL;
        int err = 0;
        if (!(hw_frames_ref = av_hwframe_ctx_alloc(StEncode->hw_device_ctx))){
            av_log(nullptr, AV_LOG_ERROR, "failed to allocate the encoder hardware frame\n");
            return;
        }
        
        frames_ctx = (AVHWFramesContext *)(hw_frames_ref->data);
        frames_ctx->format    = AV_PIX_FMT_CUDA;
        frames_ctx->sw_format = AV_PIX_FMT_NV12;
        frames_ctx->width     = width;
        frames_ctx->height    = height;
        frames_ctx->initial_pool_size = 20;
        if ((err = av_hwframe_ctx_init(hw_frames_ref)) < 0) {
            av_log(nullptr, AV_LOG_ERROR, "failed to initial the encoder hardware frame\n");
            av_buffer_unref(&hw_frames_ref);
            return;
        }
        StEncode->ce->hw_frames_ctx = av_buffer_ref(hw_frames_ref);
        if (StEncode->ce->hw_frames_ctx==nullptr){
            av_log(nullptr, AV_LOG_ERROR, "failed to allocate the encoder buffer\n");
        }
        av_buffer_unref(&hw_frames_ref);
    }     

    int ret = 0;
    if((ret = avcodec_open2(StEncode->ce, StEncode->codecEncode, NULL))<0){
        av_log(nullptr, AV_LOG_ERROR, "Failed to open encoder\r\n");
        return;
    }

    StEncode->hwframe = av_frame_alloc();
    if(StEncode->hwframe==nullptr){
        av_log(nullptr, AV_LOG_ERROR, "failed to allocate the encoder hardware frame\n");
    }
    StEncode->frame = av_frame_alloc();
    if(StEncode->frame==nullptr){
        av_log(nullptr, AV_LOG_ERROR, "failed to allocate the encoder frame\n");
    }
    StEncode->swframe = av_frame_alloc();
    if(StEncode->swframe==nullptr){
        av_log(nullptr, AV_LOG_ERROR, "failed to allocate the encoder frame\n");
    }
    StEncode->pkt = av_packet_alloc();
    if(StEncode->pkt==nullptr){
        av_log(nullptr, AV_LOG_ERROR, "failed to allocate the encoder packet\n");
    }
    StEncode->frame->format = g_format;
    StEncode->swframe->format = AV_PIX_FMT_NV12;
    StEncode->frame->width  = StEncode->swframe->width = StEncode->ce->width;
    StEncode->frame->height = StEncode->swframe->height = StEncode->ce->height; 
    if(av_frame_get_buffer(StEncode->frame, g_align)<0)
        av_frame_get_buffer(StEncode->frame, 1);
    if(av_frame_get_buffer(StEncode->swframe, g_align)<0)
        av_frame_get_buffer(StEncode->swframe, 1);
    av_hwframe_get_buffer(StEncode->ce->hw_frames_ctx, StEncode->hwframe, 0);

    StEncode->sws = sws_getContext(StEncode->ce->width, StEncode->ce->height, g_format,StEncode->ce->width, 
        StEncode->ce->height, AV_PIX_FMT_NV12,SWS_FAST_BILINEAR, NULL, NULL, NULL);
}
void CUDAImageEncode(void** handle, unsigned char* image_data, int image_len)
{
    _StEncode* StEncode = (_StEncode*)*handle;
    if (!StEncode->frame || !StEncode->swframe)  return;
    StEncode->frame->pts = StEncode->swframe->pts = StEncode->frame_encode_index;
    StEncode->frame_encode_index++;

    if(AV_PIX_FMT_GRAY8==g_format){
        memcpy(StEncode->swframe->data[0],image_data,image_len);
        memset(StEncode->swframe->data[1],128,image_len/2);
    }
    else{
        if(av_image_fill_arrays(StEncode->frame->data,StEncode->frame->linesize, image_data, g_format, 
            StEncode->ce->width, StEncode->ce->height,g_align)<0)
            av_image_fill_arrays(StEncode->frame->data,StEncode->frame->linesize, image_data, g_format, 
                StEncode->ce->width, StEncode->ce->height,1);
        switch(g_format){
            case AV_PIX_FMT_ARGB:
                libyuv::ARGBToNV12(StEncode->frame->data[0],StEncode->frame->linesize[0],
                    StEncode->swframe->data[0],StEncode->swframe->linesize[0],
                    StEncode->swframe->data[1],StEncode->swframe->linesize[1],
                    StEncode->frame->width,StEncode->frame->height);
                break;
            default:
                sws_scale(StEncode->sws,(const uint8_t **)StEncode->frame->data,StEncode->frame->linesize,0,StEncode->ce->height,
                    StEncode->swframe->data,StEncode->swframe->linesize);
        }
    }

    if(av_hwframe_transfer_data(StEncode->hwframe, StEncode->swframe, 0)>=0)
        Encode(handle, StEncode->hwframe);
}
void CUDAEndEncode(void** handle,bool bDelHandle = true)
{
    _StEncode* StEncode = (_StEncode*)*handle;
    Encode(handle, nullptr);
    if(StEncode->ce != nullptr) 
        avcodec_free_context(&StEncode->ce);
    if(StEncode->pkt != nullptr)
        av_free_packet(StEncode->pkt);
    if(StEncode->frame != nullptr){
        av_frame_unref(StEncode->frame);
        av_frame_free(&StEncode->frame);
    }
    if(StEncode->swframe != nullptr){
        av_frame_unref(StEncode->swframe);
        av_frame_free(&StEncode->swframe);
    }
    if(StEncode->hwframe != nullptr){
        av_frame_unref(StEncode->hwframe);
        av_frame_free(&StEncode->hwframe);
    }
    if(StEncode->sws != nullptr)
        sws_freeContext(StEncode->sws);
    if(StEncode->hw_device_ctx!=nullptr)
        av_buffer_unref(&StEncode->hw_device_ctx);
    if(StEncode && bDelHandle){
        delete StEncode;
        *handle =  nullptr;
    } 
}
#pragma endregion

extern bool g_showlog;
extern int n_hw_accels;
extern AVHWDeviceType hwtype;
void BeginEncode(void** handle, void* obj, int width, int height, int framerate, __int64 bitrate, int code, void(*encode)(void* obj, unsigned char* data, int& len))
{
    _StEncode* StEncode = *handle==nullptr?
        new _StEncode():(_StEncode*)*handle;
    StEncode->user_obj = obj;
    StEncode->recv = encode;
    *handle = (void*)StEncode;
    if(!g_showlog) av_log_set_level(AV_LOG_QUIET);
    if(n_hw_accels>0){
        if(n_hw_accels == 1){
            hwtype = AV_HWDEVICE_TYPE_NONE;
            SWBeginEncode(handle, obj, width,height,framerate,bitrate,code,encode);
        }
        else if(n_hw_accels == 2){
            hwtype = AV_HWDEVICE_TYPE_QSV;
            if(!av_hwdevice_ctx_create(&StEncode->hw_device_ctx, AV_HWDEVICE_TYPE_QSV, NULL, NULL, 0))
                QSVBeginEncode(handle, obj, width,height,framerate,bitrate,code,encode);
        }
        else if(n_hw_accels == 3){
            hwtype = AV_HWDEVICE_TYPE_CUDA;
            if(!av_hwdevice_ctx_create(&StEncode->hw_device_ctx, AV_HWDEVICE_TYPE_CUDA, NULL, NULL, 0))
                CUDABeginEncode(handle, obj, width,height,framerate,bitrate,code,encode);
        }
    }
    else{
        if((hwtype = av_hwdevice_find_type_by_name("cuda")) == AV_HWDEVICE_TYPE_CUDA 
            &&!av_hwdevice_ctx_create(&StEncode->hw_device_ctx, AV_HWDEVICE_TYPE_CUDA, NULL, NULL, 0))
            CUDABeginEncode(handle, obj, width,height,framerate,bitrate,code,encode);
        else if((hwtype = av_hwdevice_find_type_by_name("qsv")) == AV_HWDEVICE_TYPE_QSV &&
            !av_hwdevice_ctx_create(&StEncode->hw_device_ctx, AV_HWDEVICE_TYPE_QSV, NULL, NULL, 0))
            QSVBeginEncode(handle, obj, width,height,framerate,bitrate,code,encode);
        else if((hwtype = AV_HWDEVICE_TYPE_NONE) == AV_HWDEVICE_TYPE_NONE)
            SWBeginEncode(handle, obj, width,height,framerate,bitrate,code,encode);
    }
}
void ImageEncode(void** handle, unsigned char* image_data, int image_len)
{
    if(hwtype == AV_HWDEVICE_TYPE_CUDA)
        CUDAImageEncode(handle,image_data,image_len);
    else if(hwtype == AV_HWDEVICE_TYPE_QSV)
        QSVImageEncode(handle,image_data,image_len);
    else
        SWImageEncode(handle,image_data,image_len);
}
void EndEncode(void** handle)
{ 
    if(hwtype == AV_HWDEVICE_TYPE_CUDA)
        CUDAEndEncode(handle);
    else if(hwtype == AV_HWDEVICE_TYPE_QSV)
        QSVEndEncode(handle);
    else
        SWEndEncode(handle);
}
void EndEncode(void** handle, bool bDelHandle)
{ 
    if(hwtype == AV_HWDEVICE_TYPE_CUDA)
        CUDAEndEncode(handle, bDelHandle);
    else if(hwtype == AV_HWDEVICE_TYPE_QSV)
        QSVEndEncode(handle, bDelHandle);
    else
        SWEndEncode(handle, bDelHandle);
}